package common

type LuacProto struct {
	Code         []*OpCode //最后一条必须为OP_RETURN
	Constants    []*TValue
	Protos       []LuacProto
	Upvalues     []string
	SourceName   string
	Lines        []int32
	LocVars      []int32
	UpValueNames []string
}

func (this *LuacProto) WriteToBuffer(buf *Buffer) {
	this.writeCodes(buf)
	this.writeConstants(buf)
	this.writeProtos(buf)
	this.writeLineInfo(buf)
	this.writeLocVars(buf)
	this.writeUpVars(buf)
}

func (this *LuacProto) writeCodes(buf *Buffer) {
	buf.WriteInt32(int32(len(this.Code)))
	for _, opCode := range this.Code {
		opCode.WriteToBuffer(buf)
	}
}

func (this *LuacProto) writeConstants(buf *Buffer) {
	buf.WriteInt32(int32(len(this.Constants)))
	for _, constVal := range this.Constants {
		switch constVal.tt {
		case LUA_TNUMBER:
			buf.WriteByte(constVal.tt)
			buf.WriteFloat64(constVal.value.n)
		case LUA_TBOOLEAN:
			buf.WriteByte(constVal.tt)
			if constVal.value.b {
				buf.WriteInt32(1)
			} else {
				buf.WriteInt32(0)
			}
		case LUA_TSTRING:
			buf.WriteByte(constVal.tt)
			buf.WriteString(constVal.value.s)
		default:
			panic("invalid lua constant type")
		}
	}
}

func (this *LuacProto) writeProtos(buf *Buffer) {
	buf.WriteInt32(int32(len(this.Protos)))
}

func (this *LuacProto) writeLineInfo(buf *Buffer) {
	buf.WriteInt32(int32(len(this.Lines)))
}

func (this *LuacProto) writeLocVars(buf *Buffer) {
	buf.WriteInt32(int32(len(this.LocVars)))
}

func (this *LuacProto) writeUpVars(buf *Buffer) {
	buf.WriteInt32(int32(len(this.UpValueNames)))
}

func (this *LuacProto) AddConstantBool(val bool) int {
	for idx, constVal := range this.Constants {
		if constVal.tt == LUA_TBOOLEAN && constVal.value.b == val {
			return idx
		}
	}
	this.Constants = append(this.Constants, NewBoolTValue(val))
	return len(this.Constants) - 1
}

func (this *LuacProto) AddConstantNumber(val float64) int {
	for idx, constVal := range this.Constants {
		if constVal.tt == LUA_TNUMBER && constVal.value.n == val {
			return idx
		}
	}
	this.Constants = append(this.Constants, NewFloat64TValue(val))
	return len(this.Constants) - 1
}

func (this *LuacProto) AddConstantString(val string) int {
	for idx, constVal := range this.Constants {
		if constVal.tt == LUA_TSTRING && constVal.value.s == val {
			return idx
		}
	}
	this.Constants = append(this.Constants, NewStringTValue(val))
	return len(this.Constants) - 1
}

func NewLuaCProto() *LuacProto {
	return &LuacProto{}
}
